using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DatabaseCode.Data;

namespace DatabaseCode.Maker
{
  class TableGraphMaker
  {
    public static string OutHeader(D1_Headers headers)
    {
      return @"#ifndef COMMON_TABLE_GRAPH_H_
#define COMMON_TABLE_GRAPH_H_

#include ""dataio_base.h""

namespace dataio {

class TableGraph {
public:
  bool ReferenceTables(const Enums::Table table, bool* ref_tables);
  bool ReferencedTables(const Enums::Table table, bool* refd_tables);

  bool NearTables(const bool* tables, bool* near_tables);
};

}  // namespace dataio

#endif // COMMON_TABLE_GRAPH_H_";
    }

    public static string OutSource(D1_Headers headers)
    {
      string str = "";
      str += @"#include ""table_graph.h""

namespace dataio {

namespace {

enum Orientation { kNone, kRef , kRefd };

const Orientation kOrientation[Enums::kTableNumber][Enums::kTableNumber] = 
{";
      Orientation[][] o = GetGraph(headers);
      bool is_first_i = true;
      bool is_first_j;
      string tablename = "";
      int from_count = 0;
      int to_count = 0;
      for (int i = 0; i < headers.GetTableNumber(); i++)
      {
        if (is_first_i)
        {
          is_first_i = false;
        }
        else
        {
          str += " }," + GrapghHintString(tablename, from_count, to_count);
        }
        tablename = headers.GetTablename(i);
        from_count = to_count = 0;
        str += "\r\n  { ";
        is_first_j = true;
        for (int j = 0; j < headers.GetTableNumber(); j++)
        {
          if (is_first_j)
          {
            is_first_j = false;
          }
          else
          {
            str += ", ";
          }
          switch (o[i][j])
          {
            case Orientation.None:
              str += "kNone";
              break;
            case Orientation.Ref:
              str += "kRef ";
              from_count++;
              break;
            case Orientation.Refd:
              str += "kRefd";
              to_count++;
              break;
          }
        }
      }
      str += " } " + GrapghHintString(tablename, from_count, to_count) + @"
};

}  // namespace

//ref_tables は kNone に初期化しない。
bool TableGraph::ReferenceTables(const Enums::Table table, bool*& ref_tables)
{
  if (ref_tables) return false;

  for (int i = 0; i < Enums::kTableNumber; i++)
  {
    if (kOrientation[table][i] == kRef)
    {
      ref_tables[i] = true;
      break;
    }
  }

  return true;
}
//refd_tables は kNone に初期化しない。
bool TableGraph::ReferencedTables(const Enums::Table table, bool*& refd_tables)
{
  if (refd_tables) return false;

  for (int i = 0; i < Enums::kTableNumber; i++)
  {
    if (kOrientation[table][i] == kRefd)
    {
      refd_tables[i] = true;
      break;
    }
  }

  return true;
}

//near_tables は kNone に初期化しない。
bool TableGraph::NearTables(const bool* tables, bool*& near_tables)
{
  if (!tables || !near_tables) return false;

  for (int i = 0; i < Enums::kTableNumber; i++)
  {
    if (tables[i])
    {
      for (int j = 0; j < Enums::kTableNumber; j++)
      {
        if (kOrientation[i][j]==kRef || kOrientation[i][j]==kRefd)
        {
          near_tables[i] = true;
          break;
        }
      }
    }
  }

  return true;
}

}  // namespace dataio";
      return str;
    }

    enum Orientation { None, Ref, Refd };

    //戻り値：bool[n][m]が Orientation.From なら nテーブルが mテーブルを参照してる。
    //        Orientation.To なら mテーブルが nテーブルを参照してる。
    //例  Item がItemKind を参照しているとして
    //    bool[Item][ItemKind] は Orientation.From
    //    bool[ItemKind][Item] は Orientation.To
    static Orientation[][] GetGraph(D1_Headers headers)
    {
      Orientation[][] o;
      int i, j;
      int table_num = headers.GetTableNumber();
      o = new Orientation[table_num][];
      for (i = 0; i < table_num; i++)
      {
        o[i] = new Orientation[table_num];
        for (j = 0; j < table_num; j++)
        {
          o[i][j] = Orientation.None;
        }
      }

      i = 0;
      foreach (D2_Header h in headers.Headers)
      {
        foreach (D3_Table t in h.Tables)
        {
          foreach (D4_Field f in t.Fields)
          {
            if (f.HasReference)
            {
              if (o[i][headers.GetTableNo(f.RefTable)] != Orientation.None)
              {
                Debug.AddMessageLine("TableGraphMaker o[i][headers.GetTableNo(f.RefTable)]設定済み？");
                Debug.AddMessageLine("  " + t.Name + "." + f.Variablename);
              }
              if (o[headers.GetTableNo(f.RefTable)][i] != Orientation.None)
              {
                Debug.AddMessageLine("TableGraphMaker o[headers.GetTableNo(f.RefTable)][i]設定済み？");
                Debug.AddMessageLine("  " + t.Name + "." + f.Variablename);
              }
              o[i][headers.GetTableNo(f.RefTable)] = Orientation.Ref;
              o[headers.GetTableNo(f.RefTable)][i] = Orientation.Refd;
            }
          }
          i++;
        }
      }
      return o;
    }

    // " // ItemKind(0,2)" みたいな文字列を返す。
    public static string GrapghHintString(string tablename, int from_count, int to_count)
    {
      return " // " + tablename + "(" + from_count + "," + to_count + ")";
    }
  }
}
